﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Configuration;
using System.IO;
using Dominio.Entidades;
using System.Xml;
using Dominio.Excepciones;
using InterfazPublica;

namespace Dominio.Logica
{
    public class Agencia
    {
        private static Agencia agencia;

        public static Usuarios UsuarioLogueado { get; set; }

        public static List<Juegos> ListaJuegos { get; set; }

        public static List<Assembly> ListaAssemblies { get; set; }

        public static List<IJuegos> ListaEntidades { get; set; }

        private Agencia() { }

        public static Agencia GetAgencia()
        {
            if (agencia == null)
            {
                agencia = new Agencia();
                ListaJuegos = CargarJuegos();
                ListaAssemblies = CargarDll();
                ListaEntidades = CargarAssemblies();
            }
            return agencia;
        }

        private static List<IJuegos> CargarAssemblies()
        {
            IJuegos ip = null;
            List<IJuegos> Lista = new List<IJuegos>(); ;
            foreach (Assembly a in ListaAssemblies)
            {
                foreach (Type i in a.GetTypes())
                {
                    if (typeof(IJuegos).IsAssignableFrom(i))
                    {
                        ip = (IJuegos)Activator.CreateInstance(i);
                        Lista.Add(ip);
                    }
                }
            }
            return Lista;
        }

        private static List<Juegos> CargarJuegos()
        {
            //Cargo los juegos dados de alta en la base de datos
            List<Juegos> listaJuegos = Contexto.GetContexto().Juegos.Where(o => o.Activo == true).ToList();

            return listaJuegos;
        }

        private static List<Assembly> CargarDll()
        {
            //Obtengo la ruta donde van a estar los assemblys (dll) desde el archivo app.config
            AgenciaEntities contexto = Contexto.GetContexto();
            List<string> listaAssemblys = contexto.Juegos.Select(o => o.RutaAssembly).ToList();
            List<Juegos> juegos = CargarJuegos();
            List<Assembly> ListaAssemblys = new List<Assembly>();
            Assembly a;
            foreach (var dir in listaAssemblys)
            {
                a = Assembly.LoadFile(dir);
                ListaAssemblys.Add(a);
            }
            return ListaAssemblys;
        }

        public XmlDocument RealizarApuesta(IJuegos j)
        {
            //Devuelve los datos de la apuesta, número y monto apostado
            XmlDocument retorno = j.RealizarApuesta(j);
            //GuardarApuesta(retorno);
            return retorno;
        }

        public double CorrespondePremio(IJuegos j, string idApuesta)
        {
            Guid guid;

            if (!(idApuesta.Trim().Equals("")))
            {
                try
                {
                    guid = new Guid(idApuesta);
                }
                catch (Exception)
                {
                    throw new IncorrectFormatTypeException();
                }

            }
            else
            {
                throw new EmptyPagosIdException();
            }

            return j.CorrespondePremio(guid);
        }

        public bool EfectuarPago(IJuegos j, string idApuesta, int premio)
        {
            Guid guid = new Guid(idApuesta);
            bool pago = j.EfectuarPago(guid);
            if (pago)
            {
                AgenciaEntities contexto = Contexto.GetContexto();
                Apuestas a = contexto.Apuestas.Where(o => o.Identificador == idApuesta).First();
                a.Pagado = true;
                a.Premio = premio;
                contexto.SaveChanges();
                pago = true;
            }
            return pago;
        }

        public bool ApuestaYaPagada(IJuegos j, string idApuesta)
        {
            AgenciaEntities contexto = Contexto.GetContexto();
            Apuestas a = contexto.Apuestas.Where(o => o.Identificador == idApuesta).First();
            return (bool)a.Pagado;
        }

        public void GuardarApuesta(XmlDocument xml, Juegos juego)
        {
            XmlNode xmlElem = xml.FirstChild;
            XmlNode NodoApuesta = xml.FirstChild.ChildNodes.Item(0);
            string id_apuesta = NodoApuesta.Attributes.GetNamedItem("IdApuesta").Value;
            string valores = NodoApuesta.Attributes.GetNamedItem("Valores").Value;
            string revancha = NodoApuesta.Attributes.GetNamedItem("Revancha").Value;
            string monto = NodoApuesta.Attributes.GetNamedItem("Monto").Value;

            Apuestas a = new Apuestas();
            a.Identificador = id_apuesta;
            a.MontoApostado = Int32.Parse(monto);
            a.ValoresApuesta = valores + ";" + revancha;
            a.Usuario = UsuarioLogueado.IdUsuario;
            a.Pagado = false;
            a.MontoComision = juego.Comision;
            a.Premio = 0;
            a.Juego = juego.IdJuego;
            a.Fecha = System.DateTime.Now;

            a.GuardarApuesta();

            Log.Grabar("Guardar Apuesta: ");

        }

        public List<float> RecaudacionJuego(Juegos juego)
        {
            List<float> ret = new List<float>();
            float SumaMontos = 0;
            float MontoTotal = 0;
            float Comision = 0;

            try
            {
                var MontosApostados = from a in Contexto.GetContexto().Apuestas where a.Juego == juego.IdJuego select a.MontoApostado;
                List<int> listaMontosApostados = MontosApostados.ToList();
                SumaMontos = listaMontosApostados.Sum();
            }
            catch (InvalidOperationException ex)
            {
                Log.Grabar("ERROR: No hay apuestas para el juego: " + juego.IdJuego + " error: " + ex.Message + " acción realizada por usuario: " + Agencia.UsuarioLogueado);
            }
            try
            {
                var Premios = from a in Contexto.GetContexto().Apuestas where a.Juego == juego.IdJuego select a.Premio;
                List<int> listaPremios = Premios.ToList();
                MontoTotal = listaPremios.Sum();
            }
            catch (InvalidOperationException ex)
            {
                Log.Grabar("ERROR: No hay apuestas para el juego: " + juego.IdJuego + " error: " + ex.Message + " acción realizada por usuario: " + Agencia.UsuarioLogueado);
            }
            try
            {
                float ganancias = 0;
                List<Apuestas> listaApuestas = (from a in Contexto.GetContexto().Apuestas where a.Juego == juego.IdJuego select a).ToList();
                foreach (Apuestas a in listaApuestas)
                {
                    float montoaux = a.MontoComision / (float)100;
                    ganancias = ganancias + (a.MontoApostado * montoaux);
                }
                Comision = ganancias;
            }
            catch (InvalidOperationException ex)
            {
                Log.Grabar("ERROR: No hay apuestas para el juego: " + juego.IdJuego + " error: " + ex.Message + " acción realizada por usuario: " + Agencia.UsuarioLogueado);
            }
            ret.Add(SumaMontos);
            ret.Add(MontoTotal);
            ret.Add(Comision);

            return ret;
        }

        public float GananciasAgencia(DateTime inicio, DateTime fin)
        {
            float ganancias = 0;
            var aux = from a in Contexto.GetContexto().Apuestas where (a.Fecha >= inicio && a.Fecha <= fin) select a;
            List<Apuestas> listaApuestas = aux.ToList();
            if (listaApuestas.Count > 0)
            {
                foreach (Apuestas a in listaApuestas)
                {
                    float montoaux = a.MontoComision / (float)100;
                    ganancias = ganancias + (a.MontoApostado * montoaux);
                }
            }
            else 
            { 
                throw new NoApuestasException();
            }

            return ganancias;
        }
    }
}
